#include "Pin_ch32v.hpp"
#include "ch32v30x_libs.h"

/******** 芯片结构对应 ch32v307VCT6 ********/
const int n_Pins = 100;

static const struct {
    GPIO_TypeDef *gpio;
    uint16_t pin;
}pinMap[n_Pins] = {
    {GPIOE,2},
    {GPIOE,3},
    {GPIOE,4},
    {GPIOE,5},
    {GPIOE,6},
    {0,0}, /* VBAT */
    {GPIOC,13},
    {GPIOC,14},
    {GPIOC,15},
    {0,0}, /* VSS_5 */
    {0,0}, /* VDD_5 */
    {0,0}, /* OSC_IN */
    {0,0}, /* OSC_OUT */
    {0,0}, /* NRST */
    {GPIOC,0},
    {GPIOC,1},
    {GPIOC,2},
    {GPIOC,3},
    {0,0}, /* VSSA */
    {0,0}, /* VREF- */
    {0,0}, /* VREF+ */
    {0,0}, /* VDDA */
    {GPIOA,0},
    {GPIOA,1},
    {GPIOA,2},
    {GPIOA,3},
    {0,0}, /* VSS_4 */
    {0,0}, /* VDD_4 */
    {GPIOA,4},
    {GPIOA,5},
    {GPIOA,6},
    {GPIOA,7},
    {GPIOC,4},
    {GPIOC,5},
    {GPIOB,0},
    {GPIOB,1},
    {GPIOB,2},
    {GPIOE,7},
    {GPIOE,8},
    {GPIOE,9},
    {GPIOE,10},
    {GPIOE,11},
    {GPIOE,12},
    {GPIOE,13},
    {GPIOE,14},
    {GPIOE,15},
    {GPIOB,10},
    {GPIOB,11},
    {0,0}, /* VSS_1 */
    {0,0}, /* VIO_1 */
    {GPIOB,12},
    {GPIOB,13},
    {GPIOB,14},
    {GPIOB,15},
    {GPIOD,8},
    {GPIOD,9},
    {GPIOD,10},
    {GPIOD,11},
    {GPIOD,12},
    {GPIOD,13},
    {GPIOD,14},
    {GPIOD,15},
    {GPIOC,6},
    {GPIOC,7},
    {GPIOC,8},
    {GPIOC,9},
    {GPIOA,8},
    {GPIOA,9},
    {GPIOA,10},
    {GPIOA,11},
    {GPIOA,12},
    {GPIOA,13},
    {0,0},  /* NC */
    {0,0},  /* VSS_2 */
    {0,0},  /* VDD_2 */
    {GPIOA,14},
    {GPIOA,15},
    {GPIOC,10},
    {GPIOC,11},
    {GPIOC,12},
    {GPIOD,0},
    {GPIOD,1},
    {GPIOD,2},
    {GPIOD,3},
    {GPIOD,4},
    {GPIOD,5},
    {GPIOD,6},
    {GPIOD,7},
    {GPIOB,3},
    {GPIOB,4},
    {GPIOB,5},
    {GPIOB,6},
    {GPIOB,7},
    {0,0},  /* BOOT0 */
    {GPIOB,8},
    {GPIOB,9},
    {GPIOE,0},
    {GPIOE,1},
    {0,0},  /* VSS_3 */
    {0,0}   /* VIO_3 */
};

/******** Pin_ch32v 类与实现 ********/

class Pin_ch32v : public Pin{
public:
    Pin_ch32v(GPIO_TypeDef *gpio,uint16_t pin,PinMode mode);
    virtual void write(int level);
    virtual int read();
    virtual void toggle();
private:
    GPIO_TypeDef *gpio;
    uint16_t pin;
};

Pin_ch32v::Pin_ch32v(GPIO_TypeDef *gpio,uint16_t pin,PinMode mode){
    this->gpio = gpio;
    this->pin = pin;
    
    GPIO_InitTypeDef GPIO_InitStructure = {0};
    
    if ( gpio == GPIOA ){
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
    }else if ( gpio == GPIOB ){
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
    }else if ( gpio == GPIOC ){
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
    }else if ( gpio == GPIOD ){
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD,ENABLE);
    }else if ( gpio == GPIOE ){
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE,ENABLE);
    }
    GPIO_InitStructure.GPIO_Pin = pin;
    switch ( mode ){
        case PinMode::Input_floating: GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; break;
        case PinMode::Input_pulldown: GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; break;
        case PinMode::Input_pullup: GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; break;
        case PinMode::Output: GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; break;
        case PinMode::Output_opendrain: GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; break;
    }
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(gpio,&GPIO_InitStructure);
    
}

void Pin_ch32v::write(int level){
    GPIO_WriteBit(this->gpio,this->pin,(level == 0 ? Bit_RESET : Bit_SET));
}


int Pin_ch32v::read(){
    return ( GPIO_ReadInputDataBit(this->gpio,this->pin) == (uint8_t)Bit_RESET ) ? 0 : 1;
}

void Pin_ch32v::toggle(){
    if ( this->read() ){
        this->write(0);
    }else{
        this->write(1);
    }
}

/******** 工厂类实现 ********/
Pin *GPIOA_Pins[16] = {nullptr};
Pin *GPIOB_Pins[16] = {nullptr};
Pin *GPIOC_Pins[16] = {nullptr};
Pin *GPIOD_Pins[16] = {nullptr};
Pin *GPIOE_Pins[16] = {nullptr};

Pin *PinFactory::getPin(int pinnum,PinMode mode){
    GPIO_TypeDef *gpio;
    uint16_t pin;
    if ( pinnum <= 0 || pinnum > n_Pins ){
        return nullptr;
    }
    gpio = pinMap[pinnum - 1].gpio;
    pin = pinMap[pinnum - 1].pin;
    if ( gpio == GPIOA ){
        if ( GPIOA_Pins[pin] == nullptr ){
            GPIOA_Pins[pin] = new Pin_ch32v(gpio,(0x01 << pin),mode);
        }
        return GPIOA_Pins[pin];
    }else if ( gpio == GPIOB ){
        if ( GPIOB_Pins[pin] == nullptr ){
             GPIOB_Pins[pin] = new Pin_ch32v(gpio,(0x01 << pin),mode);
        }
        return GPIOB_Pins[pin];
    }else if ( gpio == GPIOC ){
        if ( GPIOC_Pins[pin] == nullptr ){
            GPIOC_Pins[pin] = new Pin_ch32v(gpio,(0x01 << pin),mode);
        }
        return GPIOC_Pins[pin];
    }else if ( gpio == GPIOD ){
        if ( GPIOD_Pins[pin] == nullptr ){
            GPIOD_Pins[pin] = new Pin_ch32v(gpio,(0x01 << pin),mode);
        }
        return GPIOD_Pins[pin];
    }else if ( gpio == GPIOE ){
        if ( GPIOE_Pins[pin] == nullptr ){
            GPIOE_Pins[pin] = new Pin_ch32v(gpio,(0x01 << pin),mode);
        }
        return GPIOE_Pins[pin];
    }
    return nullptr;
}


Pin *PinFactory::getPin(GPIO gpio,int pin,PinMode mode){
    if ( pin < 0 || pin > 15 ) return nullptr;
    switch ( gpio ){
    case GPIO::A:
        if ( GPIOA_Pins[pin] == nullptr ){
            GPIOA_Pins[pin] = new Pin_ch32v(GPIOA,(0x01 << pin),mode);
        }
        return GPIOA_Pins[pin];
        break;
    case GPIO::B:
        if ( GPIOB_Pins[pin] == nullptr ){
            GPIOB_Pins[pin] = new Pin_ch32v(GPIOB,(0x01 << pin),mode);
        }
        return GPIOB_Pins[pin];
        break;
    case GPIO::C:
        if ( GPIOC_Pins[pin] == nullptr ){
            GPIOC_Pins[pin] = new Pin_ch32v(GPIOC,(0x01 << pin),mode);
        }
        return GPIOC_Pins[pin];
        break;
    case GPIO::D:
        if ( GPIOD_Pins[pin] == nullptr ){
            GPIOD_Pins[pin] = new Pin_ch32v(GPIOD,(0x01 << pin),mode);
        }
        return GPIOD_Pins[pin];
        break;
    case GPIO::E:
        if ( GPIOE_Pins[pin] == nullptr ){
            GPIOE_Pins[pin] = new Pin_ch32v(GPIOE,(0x01 << pin),mode);
        }
        return GPIOE_Pins[pin];
        break;
    }
    return nullptr;
}